﻿using System;
using System.Net;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;

using Microsoft.SPOT.Touch;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.IO;



namespace Project
{
    public class Program : Microsoft.SPOT.Application
    {

        private const int port = 6969;

        public static void Main()
        {
            // Create the object that configures the GPIO pins to buttons.
            //GPIOButtonInputProvider inputProvider = new GPIOButtonInputProvider(null);
            Microsoft.SPOT.Hardware.Utility.SetLocalTime(new DateTime(2012, 10, 15, 12, 0, 0));            
            
            Drivers.createInstance();
            new Thread(startServer).Start();
            new Thread(checkRules).Start();

            Program myApplication = new Program();     

            Touch.Initialize(myApplication);

            // Start the application
            myApplication.Run(myApplication.CreateWindow());
        }

        private Window mainWindow;

        public Window CreateWindow()
        {
            // Create a window object and set its size to the
            // size of the display.
            mainWindow = new Window();
            mainWindow.Height = SystemMetrics.ScreenHeight;
            mainWindow.Width = SystemMetrics.ScreenWidth;
            
            Canvas canvas = new Canvas();
         
            Image image_light = new Image(Resources.GetBitmap(Resources.BitmapResources.light));
            image_light.HorizontalAlignment = HorizontalAlignment.Center;            
            Button button_light = new Button(image_light);
            button_light.Width = mainWindow.Width / 2;
            button_light.Height = 2*mainWindow.Height / 5;
            Canvas.SetTop(button_light, 0);
            Canvas.SetLeft(button_light, 0);
            canvas.Children.Add(button_light);
           
            Image image_thermo = new Image(Resources.GetBitmap(Resources.BitmapResources.thermometer));
            image_thermo.HorizontalAlignment = HorizontalAlignment.Center;           
            Button button_thermo = new Button(image_thermo);
            button_thermo.Height = 2 * mainWindow.Height / 5;
            button_thermo.Width = mainWindow.Width / 2;
            Canvas.SetTop(button_thermo, 0);
            Canvas.SetLeft(button_thermo, mainWindow.Width / 2);
            canvas.Children.Add(button_thermo);
          
            Image image_window = new Image(Resources.GetBitmap(Resources.BitmapResources.window));
            image_window.HorizontalAlignment = HorizontalAlignment.Center;
            Button window_button = new Button(image_window);
            window_button.Width = mainWindow.Width / 2;
            window_button.Height = 2 * mainWindow.Height / 5;
            Canvas.SetTop(window_button, 2 * mainWindow.Height / 5);
            Canvas.SetLeft(window_button, 0);
            canvas.Children.Add(window_button);
          
            Image image_irrigation = new Image(Resources.GetBitmap(Resources.BitmapResources.watering));
            image_irrigation.HorizontalAlignment = HorizontalAlignment.Center;
            Button irrigation_button = new Button(image_irrigation);
            irrigation_button.Width = mainWindow.Width / 2;
            irrigation_button.Height = 2 * mainWindow.Height / 5;
            Canvas.SetTop(irrigation_button, 2 * mainWindow.Height / 5);
            Canvas.SetLeft(irrigation_button, mainWindow.Width / 2);
            canvas.Children.Add(irrigation_button);

            Image image_info = new Image(Resources.GetBitmap(Resources.BitmapResources.info));
            image_info.HorizontalAlignment = HorizontalAlignment.Center;
            image_info.VerticalAlignment = VerticalAlignment.Center;
            Button button_info = new Button(image_info);
            button_info.Width = mainWindow.Width / 2;
            button_info.Height = mainWindow.Height / 5;
            Canvas.SetBottom(button_info, 0);
            Canvas.SetLeft(button_info, 0);
            canvas.Children.Add(button_info);

            Image image_settings = new Image(Resources.GetBitmap(Resources.BitmapResources.settings));
            image_settings.HorizontalAlignment = HorizontalAlignment.Center;
            image_settings.VerticalAlignment = VerticalAlignment.Center;
            Button button_settings = new Button(image_settings);
            button_settings.Width = mainWindow.Width / 2;
            button_settings.Height = mainWindow.Height / 5;
            Canvas.SetBottom(button_settings, 0);
            Canvas.SetRight(button_settings, 0);
            canvas.Children.Add(button_settings);
                       
           
            button_light.Click += new EventHandler(lightButton_Click);
            button_thermo.Click += new EventHandler(thermoButton_Click);
            window_button.Click += new EventHandler(windowButton_Click);
            irrigation_button.Click += new EventHandler(irrigationButton_Click);
            button_info.Click += new EventHandler(infoButton_Click);
            button_settings.Click += new EventHandler(settingsButton_Click);


            mainWindow.Child = canvas ;
            
            
            // Connect the button handler to all of the buttons.
            mainWindow.AddHandler(Buttons.ButtonUpEvent, new RoutedEventHandler(OnButtonUp), false);

            // Set the window visibility to visible.
            mainWindow.Visibility = Visibility.Visible;

            // Attach the button focus to the window.
            Buttons.Focus(mainWindow);

            return mainWindow;
        }

        void lightButton_Click(object sender, EventArgs evt)
        {
            Debug.Print("light button pressed");
            LightsWindow lw = new LightsWindow(mainWindow);         
        }

        void thermoButton_Click(object sender, EventArgs evt)
        {
            Debug.Print("Temperature button pressed");
            TemperatureWindow tw = new TemperatureWindow(mainWindow);
        }

        void windowButton_Click(object sender, EventArgs evt)
        {
            Debug.Print("Window button pressed");
            WindowWindow ww = new WindowWindow(mainWindow);
        }

        void irrigationButton_Click(object sender, EventArgs evt)
        {
            Debug.Print("irrigation button pressed");
            IrrigationWindow iw = new IrrigationWindow(mainWindow);
        }

        void infoButton_Click(object sender, EventArgs evt)
        {
            Debug.Print("info button pressed");
            InfoWindow iw = new InfoWindow(mainWindow);
        }

        void settingsButton_Click(object sender, EventArgs evt)
        {
            Debug.Print("settings button pressed");
            SettingsWindow sw = new SettingsWindow(mainWindow);
        }

        private void OnButtonUp(object sender, RoutedEventArgs evt)
        {
            ButtonEventArgs e = (ButtonEventArgs)evt;

            // Print the button code to the Visual Studio output window.
            Debug.Print(e.Button.ToString());
        }
        
        private static void startServer()
        {
            const Int32 c_port = 6969;

            // Create a socket, bind it to the server's port, and listen for client 
            // connections.
            Socket server = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, c_port);
            server.Bind(localEndPoint);
            server.Listen(Int32.MaxValue);
            while (true)
            {
                // Wait for a client to connect.
                Socket clientSocket = server.Accept();
                Debug.Print("Client Connected");
                ComManager cm = new ComManager(clientSocket);
                new Thread(cm.run).Start();                                
            }
        }

        private static void checkRules()
        {
            while (true)
            {
                string[] light_rules;
                string[] irrigation_rules;

                string temp_rule = Drivers.getInstance().tempRules;

                if (Drivers.getInstance().lightRules != "")
                {
                    light_rules = Drivers.getInstance().lightRules.Split('|');
                    LigthControl(light_rules);
                }

                if (Drivers.getInstance().irrigationRules != "")
                {
                    irrigation_rules = Drivers.getInstance().irrigationRules.Split('|');
                    IrrigationControl(irrigation_rules);
                }

                if (temp_rule != "")
                {
                    TemperatureControl(temp_rule);
                }

                if (Drivers.getInstance().Alert)
                {
                    sendAndroidNotification("eHouse", "Alarm: Intruders");
                    sendWindowsNotification("eHouse", "Alarm: Intruders");
                    //this.Dispatcher.BeginInvoke(new DispatcherOperationCallback((object o) => { onAlarm(); return o; }), null);
                    Drivers.getInstance().Alert = false;
                }

                //Thread.Sleep(600000);
                Thread.Sleep(10000);
            }
        }

        private static void TemperatureControl(string rule)
        {
            double desiredTemp = double.Parse(rule.Split('\\')[1]);
            double actualTemp = Drivers.getInstance().getTemperature();

            if (desiredTemp >= actualTemp + 2)
            {
                Drivers.getInstance().led_AC.Write(false);
                Drivers.getInstance().led_heating.Write(true);
            }
            else if (desiredTemp <= actualTemp - 2)
            {
                Drivers.getInstance().led_AC.Write(true);
                Drivers.getInstance().led_heating.Write(false);
            }
            else
            {
                Drivers.getInstance().led_AC.Write(false);
                Drivers.getInstance().led_heating.Write(false);
            }
        }

        private static void LigthControl(string[] rules)
        {
            foreach (string rule in rules)
            {
                if (rule != "")
                {
                    string[] rule_parts = rule.Split('\\');
                    string time = rule_parts[1];
                    string state = rule_parts[3];
                    int hour = int.Parse(time.Split(':')[0]);
                    int min = int.Parse(time.Split(':')[1]);
                    DateTime ruleTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, System.DateTime.Now.Day, hour ,min, 0);

                    if (DateTime.Now.Add(new TimeSpan(0, 5, 0)) > ruleTime && DateTime.Now.Subtract(new TimeSpan(0, 5, 0)) < ruleTime)
                    {
                        string[] rooms = rule_parts[2].Split('/');

                        if(rooms[1]=="TRUE")
                            Drivers.getInstance().led_kitchen.Write(state == "TRUE");
                        if (rooms[3] == "TRUE")
                            Drivers.getInstance().led_living.Write(state == "TRUE");
                        if (rooms[5] == "TRUE")
                            Drivers.getInstance().led_bedroom.Write(state == "TRUE");
                        if (rooms[7] == "TRUE")
                            Drivers.getInstance().led_bathroom.Write(state == "TRUE");
                    }

                }
            }
        }

        private static void IrrigationControl(string[] rules)
        {
            foreach (string rule in rules)
            {
                if (rule != "")
                {
                    string time = rule.Split('\\')[1];
                    DateTime ruleTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, System.DateTime.Now.Day,int.Parse(time.Split(':')[0]), int.Parse(time.Split(':')[1]), 0);

                    if (DateTime.Now.Add(new TimeSpan(0, 5, 0)) > ruleTime && DateTime.Now.Subtract(new TimeSpan(0, 5, 0)) < ruleTime)
                    {
                        Drivers.getInstance().led_irrigation.Write(rule.Split('\\')[2] == "TRUE");
                    }
                }
            }
        }

        private static void sendAndroidNotification(string title, string msg)
        {
            try
            {
                String registrationID = Drivers.getInstance().RegitrationID;
                String authKey = Drivers.getInstance().AuthKey;

                String[] registrationsIDs = registrationID.Split('|');
                String IDs = "";
                foreach (string ID in registrationsIDs)
                {
                    IDs += "\"" + ID + "\",";
                }
                IDs.Substring(0, IDs.Length - 1);

                HttpWebRequest request = (HttpWebRequest)
                     WebRequest.Create("https://android.googleapis.com/gcm/send");
                request.KeepAlive = false;
                request.ProtocolVersion = HttpVersion.Version10;
                request.Method = "POST";

                string json = "{\"data\": {\"title\":\"" + title + "\",";
                json += "\"content\":\"" + msg + "\",";
                json += "\"badge\":\"" + "0" + "\"},";
                json += "\"registration_ids\": [" + IDs + "] }";

                // turn our request string into a byte stream
                byte[] postBytes = Encoding.UTF8.GetBytes(json);

                // this is important - make sure you specify type this way
                request.Headers.Add("Authorization", "key=" + authKey);
                request.ContentType = "application/json";
                request.Accept = "application/json";
                request.ContentLength = postBytes.Length;
                Stream requestStream = request.GetRequestStream();

                // now send it
                requestStream.Write(postBytes, 0, postBytes.Length);
                requestStream.Close();

                // grab te response and print it out to the console along with the status code
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                string result;
                using (StreamReader rdr = new StreamReader(response.GetResponseStream()))
                {
                    result = rdr.ReadToEnd();
                    Debug.Print(result);
                }

                request.Dispose();
                response.Close();
                requestStream.Dispose();
            }
            catch (Exception)
            {
                Debug.Print("Unable to send Android Notification");
            }
        }

        private static void sendWindowsNotification(string title, string subtitle)
        {
            string stringUris = Drivers.getInstance().WindowsPhoneUri;

            if (stringUris != "")
            {
                string[] Uris = stringUris.Split('|');
                foreach (string subscriptionUri in Uris)
                {
                    try
                    {
                        // Get the URI that the Microsoft Push Notification Service returns to the push client when creating a notification channel.
                        // Normally, a web service would listen for URIs coming from the web client and maintain a list of URIs to send
                        // notifications out to.



                        HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);

                        // Create an HTTPWebRequest that posts the toast notification to the Microsoft Push Notification Service.
                        // HTTP POST is the only method allowed to send the notification.
                        sendNotificationRequest.Method = "POST";

                        // The optional custom header X-MessageID uniquely identifies a notification message. 
                        // If it is present, the same value is returned in the notification response. It must be a string that contains a UUID.
                        // sendNotificationRequest.Headers.Add("X-MessageID", "<UUID>");

                        // Create the toast message.
                        string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                        "<wp:Notification xmlns:wp=\"WPNotification\">" +
                           "<wp:Toast>" +
                                "<wp:Text1>" + title + "</wp:Text1>" +
                                "<wp:Text2>" + subtitle + "</wp:Text2>" +
                                "<wp:Param>/Alarm.xaml?Message=" + subtitle + "</wp:Param>" +
                           "</wp:Toast> " +
                        "</wp:Notification>";

                        // Set the notification payload to send.
                        byte[] notificationMessage = Encoding.UTF8.GetBytes(toastMessage);

                        // Set the web request content length.
                        sendNotificationRequest.ContentLength = notificationMessage.Length;
                        sendNotificationRequest.ContentType = "text/xml";
                        sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
                        sendNotificationRequest.Headers.Add("X-NotificationClass", "2");

                        //TILE NOTIFICATION

                        //      string tileMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                        //"<wp:Notification xmlns:wp=\"WPNotification\">" +
                        //    "<wp:Tile>" +
                        //      "<wp:BackgroundImage>" + "red.jpg" + "</wp:BackgroundImage>" +
                        //      "<wp:Count>" + "1" + "</wp:Count>" +
                        //      "<wp:Title>" + title + "</wp:Title>" +
                        //      "<wp:BackBackgroundImage>" + "blue.jpg" + "</wp:BackBackgroundImage>" +
                        //      "<wp:BackTitle>" + subtitle + "</wp:BackTitle>" +
                        //      "<wp:BackContent>" + "" + "</wp:BackContent>" +
                        //   "</wp:Tile> " +
                        //"</wp:Notification>";

                        //      // Set the notification payload to send.
                        //      byte[] notificationMessage = Encoding.UTF8.GetBytes(tileMessage);

                        //      // Set the web request content length.
                        //      sendNotificationRequest.ContentLength = notificationMessage.Length;
                        //      sendNotificationRequest.ContentType = "text/xml";
                        //      sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "token");
                        //      sendNotificationRequest.Headers.Add("X-NotificationClass", "1");

                        using (Stream requestStream = sendNotificationRequest.GetRequestStream())
                        {
                            requestStream.Write(notificationMessage, 0, notificationMessage.Length);
                        }

                        // Send the notification and get the response.
                        HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
                        string notificationStatus = response.Headers["X-NotificationStatus"];
                        string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
                        string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];

                        // Display the response from the Microsoft Push Notification Service.  
                        // Normally, error handling code would be here. In the real world, because data connections are not always available,
                        // notifications may need to be throttled back if the device cannot be reached.
                        Debug.Print(notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus);
                    }
                    catch (Exception ex)
                    {
                        //Debug.Print("Exception caught sending update: " + ex.ToString());
                        Debug.Print("Unable to send WP notification");
                    }
                }
            }
        }

    }
}
